package cn.tedu.v7;

import javax.management.ObjectName;
import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

/**
 * Version 5   登录与退出
 */
public class Server {
    private static Map<String, String> userDatabase;//线程安全
    private List<PrintWriter> onlineUsers=new ArrayList();
    private List onlineUsers1=new ArrayList();
    private static final Integer PORT=3333;
    private ServerSocket serverSocket;
    private Socket socket;
    private String ip;

    private List<PrintWriter> allOut=new ArrayList<>();

    public Server() {
        try {
            serverSocket = new ServerSocket(PORT);
            //创建Map对象
            userDatabase=new HashMap<>();
            userDatabase.put("user1", "password123");
            userDatabase.put("admin", "admin123");
        } catch (BindException e) {
            System.out.println("请检查端口号，被占用");
        } catch (IOException e) {
            System.out.println("客户端未进入");
        }

    }
    public void start() {

      try {
       while (true) {

       System.out.println("等待客户端链接");
        socket = serverSocket.accept();
         ip=socket.getInetAddress().getHostAddress();
           new Thread(new ClientHandler(socket)).start();

          }
          } catch(IOException e){
          System.out.println("客户端" + ip + "连接失效了");
         } finally{
         if(serverSocket!=null&&!serverSocket.isClosed()){
           try {
             serverSocket.close();
         } catch (IOException e) {
            System.out.println("服务器关闭失败");
           }
           System.out.println("服务器关闭成功");
            }
           }
       }


    public static void main(String[] args) {
        new Server().start();}
    //5.5 因为用户账号可能有很多，封装Login，传入参数判断

    //同步：broadcastMessage() 消息广播方法
    //参数:String  message  转发给客户端 文本
    //增强for：目的--来一个客户端--遍历一个客户端，给这个客户端+消息
 private synchronized void broadcastMessage(String message){
        //allOut  是集合---遍历：增强for，泛型：PrintWriter
        for(PrintWriter pw:allOut){
            pw.println("消息广播："+message);
        }
    }
    private synchronized void updateOnlineUsers(String userList){
        for(PrintWriter pw:onlineUsers){
            pw.println(userList);
        }
    }

    private class ClientHandler implements Runnable {
        private Socket socket;
        private String ip;
        private BufferedReader br;
        private PrintWriter pw;
        //4.线程中：确定哪个客户端---赋予功能：
        private String username;
        private String password;

        public ClientHandler(Socket s) {
            this.socket = s;
            ip = socket.getInetAddress().getHostAddress();

            try {
                br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
                pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8")), true);
                //来一个客户端，集合添加可以写出的方法
                synchronized (allOut) {
                    allOut.add(pw);//目的：客户端，写出消息---分发消息
                }
                synchronized (onlineUsers) {
                    onlineUsers.add(pw);//目的：客户端，写出消息---分发消息
                }
            } catch (IOException e) {
                System.out.println("客户端断开连接");
            }
        }
        private boolean Login(String user,String pass){
            return userDatabase.containsKey(user)&&userDatabase.get(user).equals(pass);
        }

        //6 handleChat  封装：读写功能 --放入线程当中
        private void handleChat(){
            onlineUsers1= Collections.singletonList(username);
            String userList="在线用户："+String.join("、",onlineUsers1);
            System.out.println(ip+"客户端连接成功");
            updateOnlineUsers(userList);
            new Thread(() -> {
                try {
                    String message;
                    while ((message = br.readLine()) != null) {
                        System.out.println(username+":"+ message);
                        //理解：客户端写出--服务器端读入
                        //      转发消息：客户端写出--服务器读入的内容
                        broadcastMessage(message);
                        pw.println("(exit退出)客户端请输入： ");
                    }
                } catch (IOException e) {
                    System.out.println(username+"客户端离开");
                }
            }).start();
            System.out.println( "服务器端用户：请输入文字");
            Scanner input = new Scanner(System.in);
              while (true) {
                      String say = input.nextLine();
                      pw.println("服务器端说:" + say);
                      System.out.println("服务器端请输入： ");

              }
        }
        public void run() {
            try {
                pw.println("请输入用户名:");
                username = br.readLine();
                pw.println("请输入密码：");
                password = br.readLine();
                if (Login(username,password)) {
                    pw.println("登陆成功，欢迎");
                    handleChat();
                } else {
                    pw.println("用户名或者密码错误");
                }

            }catch (IOException e) {
                e.printStackTrace();
            }finally {
                //一个客户端已经转发完毕了，移除当前客户端的写出功能
                    synchronized (allOut) {//目的：移除，刚刚连接客户端
                        allOut.remove(pw);
                    }
                synchronized (onlineUsers) {//目的：移除，刚刚连接客户端
                    onlineUsers.remove(pw);
                }
                    if (pw != null)
                        pw.close();
                    if (br != null)
                        try {
                            br.close();
                        } catch (IOException e) {
                            System.out.println("服务器端读入异常");
                        }
                    if (socket != null && !socket.isClosed()) {
                        System.out.println(ip + "客户端退出服务器连接");
                        try {
                            socket.close();
                        } catch (IOException e) {
                            System.out.println(ip + "客户端退出链接异常");
                        }
                    }
                }

            }
        }
    }
